package com.sd365.gateway.authentication.service.impl;

import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSONObject;
import com.sd365.common.util.TokenUtil;
import com.sd365.gateway.authentication.service.AuthenticationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
/**
 * @Class AuthenticationServiceImpl
 * @Description abel.zhan 2022-10-12 重构认证代码
 * @Author Administrator
 * @Date 2022-10-12  20:08
 * @version 1.0.0
 */
@Slf4j
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
    /**
     * 定义了认证接口的URL
     */
    public static final   String AUTH_URL = "http://sd365-permission-center/permission/centre/v1/user/auth?code=%s&account=%s&password=%s";
    /**
     * RedisTemplateConfig 中生成
     */
    @Resource(name = "tokenRedisTemplate")
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * CurrentServiceTemplateBeanConfig.java中定义
     */
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 默认设置三天过期 单位毫秒 1000 * 60 * 60 * 24 * 3 = 86400
     */
    @Value("${jwt.period}")
    private Long period = 259200000L;
    /**
     * 一天
     */
    private static final Long ONE_DAY = 86400000L;
    private static final String USER_TOKEN_KEY = "user:token:";

    @Override
    public String getToken(String code, String account, String password) {
        // 获取 request
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = attributes.getRequest();
        log.debug("HttpServletRequest request ====>{}", attributes.getRequest());
        // 用户中心返回的CommonResponse 包含 UserVO对象
        LinkedHashMap authResult = null;
        try {
            // 由于restTemplate请求会导致header丢失，所以手动添加header
            HttpHeaders headers = new HttpHeaders();
            headers.set(HttpHeaders.USER_AGENT,request.getHeader("USER-AGENT"));
            HttpEntity entity = new HttpEntity<>(null, headers);
            authResult = restTemplate.exchange(String.format(AUTH_URL, code, account, password), HttpMethod.GET, entity, LinkedHashMap.class).getBody();
            return createJWTToken(code, account, authResult);
        } catch (Exception ex) {
            log.info("authentication  error", ex);
            throw ex;
        }

    }

    /**
     * @Description: 生成具体的JWT token生成的算法
     * @Author: Administrator
     * @DATE: 2022-10-12  20:14
     * @param: 账号密码 以及用户的租户机构公司等信息
     * @return: 生成的JWT TOKEN
     */
    private String createJWTToken(String code, String account, LinkedHashMap authResult) {
        /**
         *  用户中心返回new UserVO  ，UserVO的code值 1 代表认证通过 0 代表租户错误 2 代码账号或者密码错误
         */
        final LinkedHashMap body = (LinkedHashMap) authResult.get("body");// 这个就是 CommonResponse的body UserVO
        // 即是UserVO中的code
        Integer businessCode=(Integer) (body).get("code");
        // 如果认证没通过就先返回 authResult{head:{code,message},body:{userVO:{code,message,data:{token } roleids:[]}}}
        if (businessCode != HttpStatus.HTTP_OK) {
            return new JSONObject(authResult).toJSONString();
        }
            //if(null==body.get("roleIds")) throw new RuntimeException("角色无权限");
            ArrayList roleIds = (ArrayList) (body).get("roleIds");
            Long userId = Long.parseLong((String) body.getOrDefault("id", "-1"));
            Long tenantId = Long.parseLong((String) body.getOrDefault("tenantId", "-1"));
            Long companyId = Long.parseLong((String) body.getOrDefault("companyId", "-1"));
            Long orgId = Long.parseLong((String) body.getOrDefault("orgId", "-1"));
            String userName = (String) body.getOrDefault("name", "-1");


        final JSONObject header = new JSONObject();
        final JSONObject playLoad = new JSONObject();
        header.put("alg", "HS256");
        header.put("typ", "JWT");
        playLoad.put("account", account);
        playLoad.put("roleIds", roleIds);
        playLoad.put("tenantId", tenantId);
        playLoad.put("companyId", companyId);
        playLoad.put("orgId", orgId);
        playLoad.put("userId", userId);
        playLoad.put("userName", userName);
        playLoad.put("code", code);
        // 设置token 3天过期
        playLoad.put("expiresAt",new Date(System.currentTimeMillis() + period));
        String jwtProtocol = header.toJSONString() + "." + playLoad.toJSONString();
        ((LinkedHashMap) (body).get("data")).put("token", TokenUtil.encoderToken(jwtProtocol));
        redisTemplate.opsForValue().set(userId.toString(), "userId:" + userId.toString(), 1L, TimeUnit.DAYS);;
        // 设置redis中token时间比实际时间多一个小时
        String encoderToken = TokenUtil.encoderToken(jwtProtocol);
        stringRedisTemplate.opsForValue().set(USER_TOKEN_KEY + userId, encoderToken , period + ONE_DAY, TimeUnit.MILLISECONDS);
        // 返回的报文带了JWT规范加密后的token
        return new JSONObject(authResult).toJSONString();
    }
}
